
"""
 * Copyright 2009 Yogi KENT
 * 
 * This file is main part of Sotetsu.
 * 
 * Sotetsu is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * Sotetsu is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.

"""

import logging
import re
import curses
import curses.ascii as ca
from . import wcwidth

def getChar(win, keyname=True):
	"""get a character or symbol.

	This function read one character from stdin.
	Unlike curses.window.getch(), this function correctly performs multibyte
	characters and can return string "あ".
	When a control character is occured, this keyname which has more than one
	character is returned if keyname is true, or a keyconstant which is defined
	in curses module is returned otherwise.
	"""
	b = bytearray(4)
	i=0
	while True:
		b0 = win.getch()
		if b0 > 255:
			if keyname:
				curses.ungetch(b0)
				return win.getkey()
			else: return b0
		b[i] = b0
		i+=1
		try:
			rtn = b[:i].decode()
			return rtn
		except:
			continue

def getString(win, prompt="input: ", startyx=None):
	"""get a string from stdin.

	This Function reads a string from stdin.
	Multibyte characters is properly performed.
	if a character Backspace or delete is read, previous character is deleted
	and LEFT, ^B or ^F make cursor to move left/right.
	"""
	curses.curs_set(1)
	if not startyx: startyx = win.getyx()
	win.addstr(startyx[0],startyx[1], prompt)
	rtn = ''
	i=0
	while True:
		ch = getChar(win, False)
		if ch=='\n':
			break
		elif ch=='':
			win.addstr(startyx[0],startyx[1], " "*wcwidth.wcswidth(prompt+rtn))
			win.addstr(startyx[0],startyx[1], prompt)
			rtn = ''
			i=0
			pass
		elif (ch=='' or ch==curses.KEY_LEFT) and i>0:
			yx = list( win.getyx() )
			yx[1] -= wcwidth.wcwidth(rtn[i-1])
			if yx[1]<0:
				yx[0] -= 1
				yx[1] += win.getmaxyx()[1]
			win.move(yx[0],yx[1])
			i-=1
		elif (ch=='' or ch==curses.KEY_RIGHT) and len(rtn)-1>i:
			yx = list( win.getyx() )
			yx[1] += wcwidth.wcwidth(rtn[i])
			if win.getmaxyx()[1] < yx[1]:
				yx[0] += 1
				yx[1] -= win.getmaxyx()[1]
			win.move(yx[0],yx[1])
			i+=1
		elif (ch=='\x7f' or ch==curses.KEY_BACKSPACE) and i>0:
			"delete one character prev of the cursor."
			yx = list( win.getyx() )
			yx[1] -= wcwidth.wcwidth(rtn[i-1])
			if yx[1]<0:
				yx[0] -= 1
				yx[1] += win.getmaxyx()[1]
			win.addstr(yx[0],yx[1], rtn[i:]+" ")
			win.move(yx[0],yx[1])

			rtn = rtn[0:i-1] + rtn[i:]
			i-=1
		elif isinstance(ch, str):
			rtn = rtn[0:i] + ch + rtn[i:]
			win.addstr(ch)
			yx = win.getyx()
			i+=1
			win.addstr(rtn[i:])
			win.move(yx[0],yx[1])
			#win.echoch(ch)
		else:
			pass

	win.move(0, 0)
	curses.curs_set(0)
	return rtn

def keyname2int(name):
	# TODO: catch exception?
	# controll character
	if len(name)==1:
		return ord(ca.ascii(name))
	#elif len(name)!=3 or not name[2].isalpha():
		#return None

	elif name[0:2]=='S-' or name[0:2]=='s-':
		return ord(name[2].upper())
	elif name[0:2]=='C-' or name[0:2]=='c-':
		return ord(ca.ctrl(name[2]))
	elif name[0:2]=='M-' or name[0:2]=='m-':
		return ord(ca.alt(name[2]))
	else:
		key = "KEY_"+name.upper()
		if key in curses.__dict__:
			return curses.__dict__[key]
		else: return None

def cn2int(name):
	# controll character
	if name[0]=='C' and name[1]=='-':
		return ord(ca.ctrl(name[2]))
	# alt(Meta) character
	elif name[0]=='M' and name[1]=='-':
		return ord(ca.alt(name[2]))
	else:
		return ord(ca.ascii(name[0]))

def getFilter(data):
	re_cond = {}
	for (key, value) in cond.items():
		if isinstance(value,str):
			re_cond[key] = re.compile(value)
	return lambda x: matchRegex(x, re_cond)

def refineItemList(itemList, cond):
	"""refines itemlist using cond.

	itemList is sequence of items.
	cond is a dictionary which have a strings as regex hashed by TAG.
	and return refined itemlist.
	"""
	re_cond = {}
	for (key, value) in cond.items():
		if isinstance(value,str):
			re_cond[key] = re.compile(value)
	rtn = []
	for item in itemList:
		if matchRegex(item, re_cond):
			rtn.append(item)
	return rtn

def matchRegex(item, cond):
	"""check whether item matchs cond using regex.
	
	item is a dictionary which have strings hashed by TAG.
	cond is also dictionary which have RegexObject hashed by TAG.
	"""
	for (key,value) in cond.items():
		#if item[key]!=cond[key]:
		if item[key]==None or not cond[key].search(item[key]):
			return False
	return True

def matchRegexOR(item, cond):
	"""same as matchRegex but each condition is OR-ed.  """
	for (key,value) in cond.items():
		# this comparison should be replaced into regular expression check.
		#if item[key]==cond[key]:
		if cond[key].search(item[key]):
			return True
	return False

def widthsplit(str, max):
	"""splits string

	returns tuple consisting of strings which have at most
	max length.
	"""
	width = 0
	for i in range(len(str)):
		w = wcwidth.wcwidth(str[i])
		if w < 0:
			return None
		else:
			width += w

		if width > max:
			return (str[:i],) + widthsplit(str[i:], max)
	return (str,)

NONETXT="(None)"
def widthpad(str, max):
	# TODO: Exception catch
	if str==None:
		logging.info("widthpad performs None object as string")
		return NONETXT+" "*(max-len(NONETXT))
	result = ljust(str, max)
	if result!=None:
		return result
	else:
		return NONETXT+" "*(max-len(NONETXT))

def widthcut(str, max):
	width = 0
	for i in range(len(str)):
		w = wcwidth.wcwidth(str[i])
		if w < 0:
			return None
		else:
			width += w

		if width > max:
			return str[:i]
	return str


def ljust(str, max, fillchar=" "):
	if wcwidth.wcwidth(fillchar)!=1:
		raise TypeError('ljust() expected fillchar as a character, '
				'but string of length %d found' % (len(fillchar),))

	width = 0
	for i in range(len(str)):
		w = wcwidth.wcwidth(str[i])
		if w < 0:
			return None

		width += w
		if width==max:
			return str[:i+1]
		elif width > max:
			return str[:i]+fillchar

	return str + fillchar*(max-width)

def rjust(str, max, fillchar=" "):
	if wcwidth.wcwidth(fillchar)!=1:
		raise TypeError('widthpad() expected fillchar as a character, '
				'but string of length %d found' % (len(fillchar),))
	width = 0
	for i in range(len(str)):
		w = wcwidth.wcwidth(str[i])
		if w < 0:
			return None

		width += w
		if width==max:
			return str[:i+1]
		elif width > max:
			return fillchar+str[:i]

	return fillchar*(max-width) + str

def widthcenter(str, width, fillchar=" "):
	w = width - wcwidth.wcswidth(str)
	i = w // 2
	return fillchar*i + str + fillchar*(w-i)

